﻿//
// STLExport.cs
//
// Author:
//       Xavier Fischer
//
// Copyright (c) 2019 
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using DEM.Net.Core;
using IxMilia.Stl;
using Microsoft.Extensions.Logging;
using SharpGLTF.Schema2;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace DEM.Net.glTF.Export
{
    public class STLExportService : ISTLExportService
    {
        private readonly ILogger<STLExportService> _logger;

        public STLExportService(ILogger<STLExportService> logger = null)
        {
            _logger = logger;
        }
        public void STLExport(MeshPrimitive mesh, string fileName, bool ascii = true, IEnumerable<Attribution> attributions = null)
        {
            try
            {
                CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
                // ...

                StlFile stlFile = new StlFile();
                stlFile.SolidName = GetAttributionExtraText(attributions);

                foreach(var triangle in mesh.EvaluateTriangles())
                {
                    stlFile.Triangles.Add(CreateStlTriangle(
                        triangle.A.GetGeometry().GetPosition()
                        , triangle.B.GetGeometry().GetPosition()
                        , triangle.C.GetGeometry().GetPosition()));
                }
                // ...
                string folder = System.IO.Path.GetDirectoryName(fileName);
                if (!System.IO.Directory.Exists(folder))
                {
                    System.IO.Directory.CreateDirectory(folder);
                }
                using (FileStream fs = new FileStream(fileName, FileMode.Create))
                {
                    stlFile.Save(fs, ascii);
                }
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "Error while exporting STL model");
                throw;
            }
            
        }

        private string GetAttributionExtraText(IEnumerable<Attribution> attributions)
        {
            string text = "Generated by elevationapi.com";
            if (attributions != null && attributions.Any())
            {
                var attributionsText = ", " + string.Join(", ", attributions.Select(a => $"{a.Subject}: {a.Text}"));
                text = string.Concat(text, attributionsText);
            }
            return text;
        }

        private StlTriangle CreateStlTriangle(Vector3 v1, Vector3 v2, Vector3 v3)
        {
            //Compute the triangle's normal
            Vector3 dir = Vector3.Normalize(Vector3.Cross(v2 - v1, v3 - v1));
            return new StlTriangle(CreateStlNormal(dir), CreateStlVertex(v1), CreateStlVertex(v2), CreateStlVertex(v3));
        }
        private StlNormal CreateStlNormal(Vector3 normal)
        {
            return new StlNormal(normal.X, normal.Y, normal.Z);
        }
        private StlVertex CreateStlVertex(Vector3 vertex)
        {
            return new StlVertex(vertex.X, vertex.Y, vertex.Z);
        }
    }
}
